# NixOS module for lighttpd web server

{ config, pkgs, ... }:

with pkgs.lib;

let

  cfg = config.services.lighttpd;

  needModRedirect = cfg.gitweb.enable;
  needModAlias = cfg.cgit.enable or cfg.gitweb.enable;
  needModSetenv = cfg.cgit.enable or cfg.gitweb.enable;
  needModCgi = cfg.cgit.enable or cfg.gitweb.enable;
  needModStatus = cfg.mod_status;
  needModUserdir = cfg.mod_userdir;

  configFile = if cfg.configText != "" then
    pkgs.writeText "lighttpd.conf" ''
      ${cfg.configText}
    ''
    else
    pkgs.writeText "lighttpd.conf" ''
      server.document-root = "${cfg.document-root}"
      server.port = ${toString cfg.port}
      server.username = "lighttpd"
      server.groupname = "lighttpd"

      # As for why all modules are loaded here, instead of having small
      # server.modules += () entries in each sub-service extraConfig snippet,
      # read this:
      #
      #   http://redmine.lighttpd.net/projects/1/wiki/Server_modulesDetails
      #   http://redmine.lighttpd.net/issues/2337
      #
      # Basically, lighttpd doesn't want to load (or even silently ignore) a
      # module for a second time, and there is no way to check if a module has
      # been loaded already. So if two services were to put the same module in
      # server.modules += (), that would break the lighttpd configuration.
      server.modules = (
          ${optionalString needModRedirect ''"mod_redirect",''}
          ${optionalString needModAlias ''"mod_alias",''}
          ${optionalString needModSetenv ''"mod_setenv",''}
          ${optionalString needModCgi ''"mod_cgi",''}
          ${optionalString needModStatus ''"mod_status",''}
          ${optionalString needModUserdir ''"mod_userdir",''}
          "mod_accesslog"
      )

      # Logging (logs end up in systemd journal)
      accesslog.use-syslog = "enable"
      server.errorlog-use-syslog = "enable"

      mimetype.assign = (
          ".html" => "text/html",
          ".htm" => "text/html",
          ".txt" => "text/plain",
          ".jpg" => "image/jpeg",
          ".png" => "image/png",
          ".css" => "text/css"
          )

      static-file.exclude-extensions = ( ".fcgi", ".php", ".rb", "~", ".inc" )
      index-file.names = ( "index.html" )

      ${if cfg.mod_userdir then ''
        userdir.path = "public_html"
      '' else ""}

      ${if cfg.mod_status then ''
        status.status-url = "/server-status"
        status.statistics-url = "/server-statistics"
        status.config-url = "/server-config"
      '' else ""}

      ${cfg.extraConfig}
    '';

in

{

  options = {

    services.lighttpd = {

      enable = mkOption {
        default = false;
        type = types.uniq types.bool;
        description = ''
          Enable the lighttpd web server.
        '';
      };

      port = mkOption {
        default = 80;
        type = types.uniq types.int;
        description = ''
          TCP port number for lighttpd to bind to.
        '';
      };

      document-root = mkOption {
        default = "/srv/www";
        type = types.str;
        description = ''
          Document-root of the web server. Must be readable by the "lighttpd" user.
        '';
      };

      mod_userdir = mkOption {
        default = false;
        type = types.uniq types.bool;
        description = ''
          If true, requests in the form /~user/page.html are rewritten to take
          the file public_html/page.html from the home directory of the user.
        '';
      };

      mod_status = mkOption {
        default = false;
        type = types.uniq types.bool;
        description = ''
          Show server status overview at /server-status, statistics at
          /server-statistics and list of loaded modules at /server-config.
        '';
      };

      configText = mkOption {
        default = "";
        type = types.string;
	example = ''...verbatim config file contents...'';
        description = ''
          Overridable config file contents to use for lighttpd. By default, use
          the contents automatically generated by NixOS.
        '';
      };

      extraConfig = mkOption {
        default = "";
        type = types.string;
        description = ''
          These configuration lines will be appended to the generated lighttpd
          config file. Note that this mechanism does not work when the manual
          <option>configText</option> option is used.
        '';
      };

    };

  };

  config = mkIf cfg.enable {

    systemd.services.lighttpd = {
      description = "Lighttpd Web Server";
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      preStart = ''
        ${if cfg.cgit.enable then ''
          mkdir -p /var/cache/cgit
          chown lighttpd:lighttpd /var/cache/cgit
        '' else ""}
      '';
      serviceConfig.ExecStart = "${pkgs.lighttpd}/sbin/lighttpd -D -f ${configFile}";
      # SIGINT => graceful shutdown
      serviceConfig.KillSignal = "SIGINT";
    };

    users.extraUsers.lighttpd = {
      group = "lighttpd";
      description = "lighttpd web server privilege separation user";
      uid = config.ids.uids.lighttpd;
    };

    users.extraGroups.lighttpd.gid = config.ids.gids.lighttpd;
  };
}
